/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { GLFrame } = require("../../engine/GLFrame");
const { DAEObject } = require("../../engine/graphics/DAEObject");
const { X3DFast, X2DBeta } = require("../../engine/graphics/X2DInterface");
const { XMat4 } = require("../../engine/graphics/XMat4");
const { XTexture } = require("../../engine/graphics/XTexture");
const { XTools } = require("../../engine/XTools");
const { AttributeArea } = require("../AttributeArea");
const { LogManager } = require("../ros1/LogManager");
const { RosProto } = require("../ros_msg_utils/RosProto");
const { SaveRobotConfig } = require("./SaveRobotConfig");

class CreateCustomPart {
    static modals = {
        "box": false,
        "cylinder": false,
        "sphere": false,
    }
    static daeModals = {};
    static on = false;
    static tmat = new XMat4();
    static coordinate = {
        mat: new XMat4(),
        pos: { x: 0, y: 0, z: 0 },
        rotate: { x: 15, y: 0, z: -15, },
        scale: 100
    };
    static coordinateImpl() {
        this.coordinate.mat.Unit();
        this.coordinate.mat.Scale(this.coordinate.scale, this.coordinate.scale, this.coordinate.scale);
        this.coordinate.mat.Rotate(0, 0, this.coordinate.rotate.z);
        this.coordinate.mat.Rotate(this.coordinate.rotate.x, 0, 0);
    }
    static reset(oldPart = null) {
        this.tmpPart = {
            name: "custom_part",
            type: "box",
            mass: 1,
            xyz: { x: 0, y: 0, z: 0 },
            rpy: { x: 0, y: 0, z: 0 },

            box: {
                x: 1,
                y: 1,
                z: 1,
            },
            cylinder: {
                radius: 0.5,
                height: 1,
            },
            sphere: {
                radius: 0.5,
            },
            mesh: {
                path: "",
                scale: { x: 1, y: 1, z: 1 },
            },
            material: "color",
            color: { r: 1, g: 1, b: 1, a: 1, },
            texture: "",
        };
        if (oldPart) {
            this.tmpPart = { ...this.tmpPart, ...oldPart };
            this.isModify = true;
        } else this.isModify = false;
        this.coordinateImpl();
    }
    static createImpl() {
        let dpart = {
            token: this.isModify ? this.tmpPart.token : XTools.GenToken(16),
        };
        dpart.name = this.tmpPart.name.trim();
        if (dpart.name.length < 3 || dpart.name.length > 32) {
            LogManager.E("部件名称长度必须在3-32之间！");
            return;
        }
        if (!XTools.isValidVariableName(dpart.name)) {
            LogManager.E("部件名称只能包含字母、数字、下划线，首字母不能为数字！");
            return;
        }
        dpart.type = this.tmpPart.type;
        dpart.mass = parseFloat(this.tmpPart.mass);
        dpart.xyz = {
            x: parseFloat(this.tmpPart.xyz.x),
            y: parseFloat(this.tmpPart.xyz.y),
            z: parseFloat(this.tmpPart.xyz.z),
        }
        dpart.rpy = {
            x: parseFloat(this.tmpPart.rpy.x),
            y: parseFloat(this.tmpPart.rpy.y),
            z: parseFloat(this.tmpPart.rpy.z),
        }
        if (dpart.type === "box") {
            dpart.box = {
                x: parseFloat(this.tmpPart.box.x),
                y: parseFloat(this.tmpPart.box.y),
                z: parseFloat(this.tmpPart.box.z),
            }
            dpart.bounds = {
                min: [
                    -0.5 * dpart.box.x + dpart.xyz.x,
                    -0.5 * dpart.box.y + dpart.xyz.y,
                    -0.5 * dpart.box.z + dpart.xyz.z],
                max: [
                    0.5 * dpart.box.x + dpart.xyz.x,
                    0.5 * dpart.box.y + dpart.xyz.y,
                    0.5 * dpart.box.z + dpart.xyz.z],
            }
        }
        if (dpart.type === "cylinder") {
            dpart.cylinder = {
                radius: parseFloat(this.tmpPart.cylinder.radius),
                height: parseFloat(this.tmpPart.cylinder.height),
            }
            dpart.bounds = {
                min: [
                    - dpart.cylinder.radius + dpart.xyz.x,
                    - dpart.cylinder.radius + dpart.xyz.y,
                    -0.5 * dpart.cylinder.height + dpart.xyz.z],
                max: [
                    dpart.cylinder.radius + dpart.xyz.x,
                    dpart.cylinder.radius + dpart.xyz.y,
                    0.5 * dpart.cylinder.height + dpart.xyz.z],
            }
        }
        if (dpart.type === "sphere") {
            dpart.sphere = {
                radius: parseFloat(this.tmpPart.sphere.radius),
            }
            dpart.bounds = {
                min: [
                    dpart.sphere.radius + dpart.xyz.x,
                    dpart.sphere.radius + dpart.xyz.y,
                    dpart.sphere.radius + dpart.xyz.z],
                max: [
                    dpart.sphere.radius + dpart.xyz.x,
                    dpart.sphere.radius + dpart.xyz.y,
                    dpart.sphere.radius + dpart.xyz.z],
            }
        }
        if (dpart.type === "mesh") {
            dpart.mesh = {
                path: this.tmpPart.mesh.path,
                scale: {
                    x: parseFloat(this.tmpPart.mesh.scale.x),
                    y: parseFloat(this.tmpPart.mesh.scale.y),
                    z: parseFloat(this.tmpPart.mesh.scale.z),
                }
            }
            dpart.bounds = {
                min: [
                    -0.5 + dpart.xyz.x,
                    -0.5 + dpart.xyz.y,
                    -0.5 + dpart.xyz.z],
                max: [
                    0.5 + dpart.xyz.x,
                    0.5 + dpart.xyz.y,
                    0.5 + dpart.xyz.z],
            }
        }
        dpart.material = this.tmpPart.material;
        if (dpart.material === "color") {
            dpart.color = {
                r: parseFloat(this.tmpPart.color.r),
                g: parseFloat(this.tmpPart.color.g),
                b: parseFloat(this.tmpPart.color.b),
                a: parseFloat(this.tmpPart.color.a),
            }
        } else if (dpart.material === "texture") {
            dpart.texture = this.tmpPart.texture;
        }

        const { SelectRobotPart } = require("./SelectRobotPart");
        SelectRobotPart.getInstance().createPart(dpart);
        this.on = false;
    }
    static updateAttributeArea() {
        AttributeArea.gi().clear();

        AttributeArea.gi().addTitle(this.isModify ? "修改自定义部件" : "创建自定义部件");
        AttributeArea.gi().addInput({
            label: "部件名称：",
            default: this.tmpPart.name,
            callback: (value) => {
                this.tmpPart.name = value;
            },
        });
        AttributeArea.gi().addDropdown({
            label: "外形:",
            list: ["box", "cylinder", "sphere", "mesh"],
            default: this.tmpPart.type,
            callback: (value) => {
                this.tmpPart.type = value;
                this.updateAttributeArea();
            },
        });
        if (this.tmpPart.type === "box") {
            AttributeArea.gi().addLabel({ label: "尺寸:" });
            AttributeArea.gi().addLabelInputs([
                {
                    label: "x:",
                    default: this.tmpPart.box.x,
                    callback: (value) => {
                        this.tmpPart.box.x = value;
                    },
                }, {
                    label: "y:",
                    default: this.tmpPart.box.y,
                    callback: (value) => {
                        this.tmpPart.box.y = value;
                    },
                }, {
                    label: "z:",
                    default: this.tmpPart.box.z,
                    callback: (value) => {
                        this.tmpPart.box.z = value;
                    },
                }
            ]);
        } else if (this.tmpPart.type === "cylinder") {
            AttributeArea.gi().addLabel({ label: "尺寸:" });
            AttributeArea.gi().addLabelInputs([
                {
                    label: "半径:",
                    default: this.tmpPart.cylinder.radius,
                    callback: (value) => {
                        this.tmpPart.cylinder.radius = value;
                    },
                }, {
                    label: "高度:",
                    default: this.tmpPart.cylinder.height,
                    callback: (value) => {
                        this.tmpPart.cylinder.height = value;
                    },
                }
            ]);
        } else if (this.tmpPart.type === "sphere") {
            AttributeArea.gi().addLabel({ label: "尺寸:" });
            AttributeArea.gi().addLabelInput({
                label: "半径:",
                default: this.tmpPart.sphere.radius,
                callback: (value) => {
                    this.tmpPart.sphere.radius = value;
                },
            });
        } else if (this.tmpPart.type === "mesh") {
            AttributeArea.gi().addInput({
                label: "dae文件路径(将dae文件拖入左侧视图区):",
                default: this.tmpPart.mesh.path,
                callback: (value) => {
                    this.tmpPart.mesh.path = value;
                },
            });
        }
        AttributeArea.gi().addInput({
            label: "质量(kg):",
            default: this.tmpPart.mass,
            callback: (value) => {
                this.tmpPart.mass = value;
            },
        })
        AttributeArea.gi().addLabel({ label: "位置:" });
        AttributeArea.gi().addLabelInputs([
            {
                label: "x:",
                default: this.tmpPart.xyz.x,
                callback: (value) => {
                    this.tmpPart.xyz.x = value;
                },
            }, {
                label: "y:",
                default: this.tmpPart.xyz.y,
                callback: (value) => {
                    this.tmpPart.xyz.y = value;
                },
            }, {
                label: "z:",
                default: this.tmpPart.xyz.z,
                callback: (value) => {
                    this.tmpPart.xyz.z = value;
                },
            }
        ]);
        AttributeArea.gi().addLabel({ label: "旋转:" });
        AttributeArea.gi().addLabelInputs([
            {
                label: "x:",
                default: this.tmpPart.rpy.x,
                callback: (value) => {
                    this.tmpPart.rpy.x = value;
                },
            }, {
                label: "y:",
                default: this.tmpPart.rpy.y,
                callback: (value) => {
                    this.tmpPart.rpy.y = value;
                },
            }, {
                label: "z:",
                default: this.tmpPart.rpy.z,
                callback: (value) => {
                    this.tmpPart.rpy.z = value;
                },
            }
        ]);
        if (this.tmpPart.type === "mesh") {
            AttributeArea.gi().addLabel({ label: "缩放:" });
            AttributeArea.gi().addLabelInputs([
                {
                    label: "x:",
                    default: this.tmpPart.mesh.scale.x,
                    callback: (value) => {
                        this.tmpPart.mesh.scale.x = value;
                    },
                }, {
                    label: "y:",
                    default: this.tmpPart.mesh.scale.y,
                    callback: (value) => {
                        this.tmpPart.mesh.scale.y = value;
                    },
                }, {
                    label: "z:",
                    default: this.tmpPart.mesh.scale.z,
                    callback: (value) => {
                        this.tmpPart.mesh.scale.z = value;
                    },
                }
            ]);
        }
        AttributeArea.gi().addDropdown({
            label: "材质类型:",
            list: this.tmpPart.type === "mesh" ? ["color", "texture"] : ["color"],
            default: this.tmpPart.material,
            callback: (value) => {
                this.tmpPart.material = value;
                this.updateAttributeArea();
            },
        });
        if (this.tmpPart.material === "color") {
            AttributeArea.gi().addLabel({ label: "颜色:" });
            AttributeArea.gi().addLabelInputs([
                {
                    label: "r:",
                    default: this.tmpPart.color.r,
                    callback: (value) => {
                        this.tmpPart.color.r = value;
                    },
                }, {
                    label: "g:",
                    default: this.tmpPart.color.g,
                    callback: (value) => {
                        this.tmpPart.color.g = value;
                    },
                }, {
                    label: "b:",
                    default: this.tmpPart.color.b,
                    callback: (value) => {
                        this.tmpPart.color.b = value;
                    },
                }, {
                    label: "a:",
                    default: this.tmpPart.color.a,
                    callback: (value) => {
                        this.tmpPart.color.a = value;
                    },
                }
            ]);
        } else if (this.tmpPart.material === "texture") {
            AttributeArea.gi().addInput({
                label: "png文件路径(将png文件拖入左侧视图区):",
                default: this.tmpPart.texture,
                callback: (value) => {
                    this.tmpPart.texture = value;
                },
            });
        }
        AttributeArea.gi().addGap(1);
        AttributeArea.gi().addButton({
            label: this.isModify ? "确定修改" : "确定创建",
            callback: () => {
                this.createImpl();
            },
        });

        AttributeArea.gi().addGap(1);
        AttributeArea.gi().addButton({
            label: "取消",
            callback: () => {
                const { SelectRobotPart } = require("./SelectRobotPart");
                SelectRobotPart.getInstance().createPart(null);
                this.on = false;
            },
        });

        AttributeArea.gi().flush();
        this.on = true;
        GLFrame.gi().RegistCallback(GLFrame.TYPE_DROPFILE, (files, x, y) => {
            CreateCustomPart.dropFile(files, x, y);
        });
    }
    static dropFile(files, x, y) {
        if (!this.on) return;
        if (this.tmpPart.type != "mesh") {
            LogManager.E("创建mesh部件时，支持拖入(.dae)模型文件和(.png)贴图文件");
            return;
        }
        for (let file of files) {
            if (file.name.endsWith(".dae")) {
                SaveRobotConfig.getInstance().sendFileToProject("dae", file, (path) => {
                    this.tmpPart.mesh.path = path;
                    this.updateAttributeArea();
                });
            }
            else if (file.name.endsWith(".png")) {
                SaveRobotConfig.getInstance().sendFileToProject("dae", file, (path) => {
                    this.tmpPart.texture = path;
                    this.updateAttributeArea();
                });
            } else {
                LogManager.E("创建mesh部件时，支持拖入(.dae)模型文件和(.png)贴图文件");
            }
        }
    }
    static onDraw() {
        X3DFast.swapMode3D(false);

        X3DFast.coordinateLine(this.coordinate.mat, 5);

        this.renderPart(this.tmpPart, this.coordinate.mat);

        X2DBeta.swapMode2D();
        X3DFast.coordinateLetter(this.coordinate.mat, 5);
    }
    static renderPart(part, coordinateMat, withBounds = false, pickPoint = 0) {
        this.tmat.Unit();
        if (part.type == "box") {
            this.tmat.Scale(part.box.x, part.box.y, part.box.z);
        } else if (part.type == "cylinder") {
            this.tmat.Scale(part.cylinder.radius * 2, part.cylinder.radius * 2, part.cylinder.height);
        } else if (part.type == "sphere") {
            this.tmat.Scale(part.sphere.radius * 2, part.sphere.radius * 2, part.sphere.radius * 2);
        } else if (part.type == "mesh") {
            this.tmat.Scale(part.mesh.scale.x, part.mesh.scale.y, part.mesh.scale.z);
        }
        this.tmat.Rotate(part.rpy.x, part.rpy.y, part.rpy.z);
        this.tmat.Move(part.xyz.x, part.xyz.y, part.xyz.z);
        this.tmat.Mult(coordinateMat);

        let color = 0xffffffff;
        if (part.material == "color") {
            let r = Math.min(Math.max(parseInt(parseFloat(part.color.r) * 255), 0), 255);
            let g = Math.min(Math.max(parseInt(parseFloat(part.color.g) * 255), 0), 255);
            let b = Math.min(Math.max(parseInt(parseFloat(part.color.b) * 255), 0), 255);
            let a = Math.min(Math.max(parseInt(parseFloat(part.color.a) * 255), 0), 255);
            color = a << 24 | r << 16 | g << 8 | b;
        }
        if (["box", "cylinder", "sphere"].includes(part.type)) {
            if (!this.modals[part.type]) {
                this.modals[part.type] = new DAEObject({ url: "dae/" + part.type + ".dae" });
            } else {
                this.modals[part.type].render({ mat: this.tmat, color: color, withBounds: withBounds, pickPoint: pickPoint });
            }
        } else if (part.type == "mesh" && part.mesh.path.length > 0 && part.mesh.path.endsWith(".dae")) {
            if (part.mesh.path in this.daeModals) {
                if (this.daeModals[part.mesh.path] == "loading") {
                } else {
                    this.daeModals[part.mesh.path].render({ mat: this.tmat, color: color, withBounds: withBounds, pickPoint: pickPoint });
                }
            } else {
                this.daeModals[part.mesh.path] = "loading";
                RosProto.getFile(part.mesh.path, (data) => {
                    if (data) {
                        // const buffer = new ArrayBuffer(data.length);
                        // const view = new Uint8Array(buffer);
                        // view.set(data);
                        const xmlData = new Uint8Array(data);
                        let s = new TextDecoder().decode(xmlData);
                        this.daeModals[part.mesh.path] = new DAEObject(
                            {
                                name: part.mesh.path,
                                xmlString: s,
                                resLoader: (resPath, callback) => {
                                    if (part.texture.length > 0 && part.texture.endsWith(resPath)) {
                                        RosProto.getFile(part.texture, (data) => {
                                            const uint8Array = new Uint8Array(data);
                                            const blob = new Blob([uint8Array], { type: 'image/png' });// 创建Blob对象
                                            const imageUrl = URL.createObjectURL(blob);// 创建对象URL

                                            const image = new Image();// 创建Image对象并加载
                                            image.onload = function () {
                                                let rid = XTexture.LoadTextureFromImage2(image);// 图像加载完成后，可以在这里创建WebGL纹理
                                                callback(rid);

                                                URL.revokeObjectURL(imageUrl);// 释放对象URL
                                            };
                                            image.onerror = function () {
                                                LogManager.E("加载贴图失败：" + part.texture);
                                            };
                                            image.src = imageUrl;
                                        });
                                    } else {
                                        LogManager.E("资源未找到：" + resPath);
                                    }
                                }
                            });
                    } else {
                        LogManager.E("加载模型失败：" + part.mesh.path);
                    }
                })
            }
        }
    }
    static pick(part, options) {
        this.tmat.Unit();
        if (part.type == "box") {
            this.tmat.Scale(part.box.x, part.box.y, part.box.z);
        } else if (part.type == "cylinder") {
            this.tmat.Scale(part.cylinder.radius * 2, part.cylinder.radius * 2, part.cylinder.height);
        } else if (part.type == "sphere") {
            this.tmat.Scale(part.sphere.radius * 2, part.sphere.radius * 2, part.sphere.radius * 2);
        } else if (part.type == "mesh") {
            this.tmat.Scale(part.mesh.scale.x, part.mesh.scale.y, part.mesh.scale.z);
        }
        this.tmat.Rotate(part.rpy.x, part.rpy.y, part.rpy.z);
        this.tmat.Move(part.xyz.x, part.xyz.y, part.xyz.z);
        this.tmat.Mult(options.mat);

        if (["box", "cylinder", "sphere"].includes(part.type)) {
            if (!this.modals[part.type]) {
                this.modals[part.type] = new DAEObject({ url: "dae/" + part.type + ".dae" });
            } else {
                if (this.modals[part.type].pick({ mat: this.tmat, v0: options.v0, v1: options.v1 })) {
                    return true;
                }
            }
        }
        return false;
    }
    static onTouch(msg, x, y) {
        if (msg == 10) {
            this.coordinate.scale = this.coordinate.scale * 1.1;
            this.coordinateImpl();
        }
        if (msg == 11) {
            this.coordinate.scale = this.coordinate.scale * 0.9;
            this.coordinateImpl();
        }
        if (this.drag) {
            if (msg == 2) {
                let dx = x - this.drag.x;
                let dy = y - this.drag.y;
                this.coordinate.rotate.z += dx / 10;
                this.coordinate.rotate.x += dy / 10;
                this.coordinateImpl();
                this.drag = {
                    x: x,
                    y: y,
                }
            } else this.drag = false;
        }

        if (msg == 1) {
            this.drag = {
                x: x,
                y: y,
            }
        }
        return true;
    }
}

module.exports = {
    CreateCustomPart
};